package pl.shenlon.poker.cards.evaluation;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.StringTokenizer;

import pl.shenlon.poker.cards.Card;
import pl.shenlon.poker.cards.CardUtils;
import pl.shenlon.poker.cards.Hand;
import pl.shenlon.poker.cards.deck.Deck;
import pl.shenlon.poker.cards.deck.PokerDeck;

public class PotOddsEvaluator {

	private static final Map<String, Map<String, Integer>> DETAILED_PRE_FLOP_ODDS;

	private static final Map<String, Double> OVERALL_PRE_FLOP_ODDS;

	public static final int POSSIBLE_CARD_COMBINATIONS_PRE_FLOP_COUNT = 2118760,
			POSSIBLE_CARD_COMBINATIONS_FLOP_COUNT = 1081,
			POSSIBLE_CARD_COMBINATIONS_TURN_COUNT = 46;

	public static String PRE_FLOP_ODDS_DATA_FILE_PATH = "/data/odds/preflop.dat";

	public static final String[] PREFIXES;

	static {
		PREFIXES = new String[]{
				"ROYAL_FLUSH",
				"STRAIGHT_FLUSH",
				"FOUR_OF_A_KIND",
				"FULL_HOUSE",
				"FLUSH",
				"STRAIGHT",
				"THREE_OF_A_KIND",
				"TWO_PAIR",
				"PAIR",
				"HIGH_CARD",};

		OVERALL_PRE_FLOP_ODDS = new HashMap<String, Double>();
		DETAILED_PRE_FLOP_ODDS = new HashMap<String, Map<String, Integer>>();
		Scanner in = new Scanner(
				PotOddsEvaluator.class
						.getResourceAsStream(PRE_FLOP_ODDS_DATA_FILE_PATH),
				"UTF-8");
		while (in.hasNextLine()) {
			String line = in.nextLine();
			int splitPosition = line.indexOf(' ');
			String hand = line.substring(0, splitPosition);
			String value = line.substring(splitPosition + 1);

			Map<String, Integer> handOdds = new HashMap<String, Integer>();
			StringTokenizer stringTokenizer = new StringTokenizer(value);
			for (String prefix : PREFIXES) {
				int count = Integer.valueOf(stringTokenizer.nextToken());
				handOdds.put(prefix, count);
			}
			DETAILED_PRE_FLOP_ODDS.put(hand, handOdds);

			splitPosition = line.lastIndexOf(' ');
			int count = Integer.valueOf(line.substring(splitPosition + 1));
			OVERALL_PRE_FLOP_ODDS.put(hand, (double) count
					/ POSSIBLE_CARD_COMBINATIONS_PRE_FLOP_COUNT);
		}
		in.close();
	}

	private final Deck deck;
	public PotOddsEvaluator() {
		deck = new PokerDeck();
	}

	public Map<String, Integer> getDetailedFlopHandOdds(Hand hand) {
		HandRankEvaluator evaluator = HandRankEvaluator.getInstance();
		Map<String, Integer> odds = new LinkedHashMap<String, Integer>();
		for (String prefix : PREFIXES) {
			odds.put(prefix, 0);
		}
		Hand testHand = new Hand(7);
		String baseHandRank = evaluator.evaluateHand(hand).toString();
		testHand.setCards(hand.getCards());
		int baseHandRankPosition = getPrefixPosition(baseHandRank);
		for (int i = 0; i < deck.getCardsInDeckCount(); i++) {
			Card firstCard = deck.getCardAtPosition(i);
			testHand.setCard(5, firstCard);
			for (int j = i + 1; j < deck.getCardsInDeckCount(); j++) {
				Card secondCard = deck.getCardAtPosition(j);
				if (hand.containsCard(firstCard) || hand.containsCard(secondCard)) {
					continue;
				}
				testHand.setCard(6, secondCard);
				String handRank = evaluator.evaluateHand(testHand).toString();
				for (int k = 0; k < baseHandRankPosition; k++) {
					if (handRank.startsWith(PREFIXES[k])) {
						odds.put(PREFIXES[k], odds.get(PREFIXES[k]) + 1);
						break;
					}
				}
			}
		}
		return odds;
	}

	public Map<String, Integer> getDetailedHandOdds(Hand hand, Hand table) {
		Hand joinedHand = CardUtils.joinHands(hand, table);
		// joinedHand.sortAscending();
		switch (joinedHand.getCardsCount()) {
			case 2 :
				return getDetailedPreFlopHandOdds(joinedHand);
			case 5 :
				return getDetailedFlopHandOdds(joinedHand);
			case 6 :
				return getDetailedTurnHandOdds(joinedHand);
			default :
				return null;
		}
	}

	public Map<String, Integer> getDetailedPreFlopHandOdds(Hand hand) {
		return DETAILED_PRE_FLOP_ODDS.get(hand.toString());
	}

	public Map<String, Integer> getDetailedTurnHandOdds(Hand hand) {
		HandRankEvaluator evaluator = HandRankEvaluator.getInstance();
		Map<String, Integer> odds = new LinkedHashMap<String, Integer>();
		for (String prefix : PREFIXES) {
			odds.put(prefix, 0);
		}
		Hand testHand = new Hand(7);
		String baseHandRank = evaluator.evaluateHand(hand).toString();
		testHand.setCards(hand.getCards());
		int baseHandRankPosition = getPrefixPosition(baseHandRank);
		for (int i = 0; i < deck.getCardsInDeckCount(); i++) {
			Card firstCard = deck.getCardAtPosition(i);
			if (hand.containsCard(firstCard)) {
				continue;
			}
			testHand.setCard(6, firstCard);
			String handRank = evaluator.evaluateHand(testHand).toString();
			for (int k = 0; k < baseHandRankPosition; k++) {
				if (handRank.startsWith(PREFIXES[k])) {
					odds.put(PREFIXES[k], odds.get(PREFIXES[k]) + 1);
					break;
				}
			}
		}
		return odds;
	}

	public double getOverallFlopHandOdds(Hand hand) {
		Map<String, Integer> odds = getDetailedFlopHandOdds(hand);
		int count = 0;
		for (int value : odds.values()) {
			count += value;
		}
		return (double) count / POSSIBLE_CARD_COMBINATIONS_FLOP_COUNT;
	}

	public double getOverallHandOdds(Hand hand, Hand table) {
		Hand joinedHand = CardUtils.joinHands(hand, table);
		// joinedHand.sortAscending();
		switch (joinedHand.getCardsCount()) {
			case 2 :
				return getOverallPreFlopHandOdds(joinedHand);
			case 5 :
				return getOverallFlopHandOdds(joinedHand);
			case 6 :
				return getOverallTurnHandOdds(joinedHand);
			default :
				return 0.0;
		}
	}

	public double getOverallPreFlopHandOdds(Hand hand) {
		return OVERALL_PRE_FLOP_ODDS.get(hand.toString());
	}

	public double getOverallTurnHandOdds(Hand hand) {
		Map<String, Integer> odds = getDetailedTurnHandOdds(hand);
		int count = 0;
		for (int value : odds.values()) {
			count += value;
		}
		return (double) count / POSSIBLE_CARD_COMBINATIONS_TURN_COUNT;
	}

	public int getPrefixPosition(String prefix) {
		for (int i = 0; i < PREFIXES.length; i++) {
			if (prefix.startsWith(PREFIXES[i])) {
				return i;
			}
		}
		return -1;
	}
}